//
// SystemCall.cpp
// Usage: System call implement
// CreateDate: 2009-05-05
// Author: MaJiuyue
//
#include <SCI.h>
#include <errno.h>
#include <RequestPackage.h>
#include "SystemCall.h"
#include "DefaultIRQAction.h"
#include "../schedule/Process.h"
#include "../fs/VFS.h"
#include "../intrman/ClockIRQAction.h"

#define SYSCALL(name) (addr_t)&SystemCall::do_##name

addr_t syscall_vec[] = {
	SYSCALL(sbrk),
	SYSCALL(getpid),
	SYSCALL(fork),
	SYSCALL(exit),
	SYSCALL(block),
	SYSCALL(wakeup),
	SYSCALL(send),
	SYSCALL(receive),

	SYSCALL(open),
	SYSCALL(close),
	SYSCALL(read),
	SYSCALL(write),
	SYSCALL(lseek),
	SYSCALL(mkdev),

	SYSCALL(gettimeofday),

	SYSCALL(MapAddress),
	SYSCALL(UnmapAddress),
	SYSCALL(RegisterIRQ),
	SYSCALL(UnRegisterIRQ),
	SYSCALL(FinishRequest),
	SYSCALL(SetPriority)
};

namespace SystemCall
{

addr_t do_sbrk(int incr)
{
	Context * context = GetCurrentProcess()->GetContext();
	return context->ExtendHeap(incr);
}

int do_getpid()
{
	return GetCurrentProcess()->GetPID();
}

pid_t do_fork()
{
	Process * cur_proc = GetCurrentProcess();
	Process * child = NULL;

	Resource * res = ResourceManager.RequestResource(
		ServiceCallInterface::ProcessManager::ResourceName
	);
	child = (Process *)res->CallSCI(
		ServiceCallInterface::ProcessManager::SCIID_CreateProcess,
		(u32)cur_proc->GetPID()
	);
	
	return child->GetPID();
}

int do_exit()
{
	Resource * res = NULL;
	Process *cur = GetCurrentProcess();
	int ret = cur->Exit();
	
	if (ret == 1)
	{
		res = ResourceManager.RequestResource(
			ServiceCallInterface::ProcessManager::ResourceName
		);						   
		res->CallSCI(ServiceCallInterface::ProcessManager::SCIID_DestroyProcess,
			cur->GetPID()
		);
	}

	res = ResourceManager.RequestResource(
		ServiceCallInterface::Scheduler::ResourceName
	);	
	res->CallSCI(
		ServiceCallInterface::Scheduler::SCIID_Schedule
	);

	return EJUSTRETURN;	

}


int do_waitpid(pid_t pid)
{
	Process *cur = GetCurrentProcess();
	int ret = cur->WaitPID(pid);
	if (ret == 1)
		return EJUSTRETURN;
	else
		return ECHILD;
}

int do_send(pid_t dst, addr_t vaddr)
{
	Resource * res = ResourceManager.RequestResource(
		ServiceCallInterface::IPCCenter::ResourceName
	);
	return res->CallSCI(
		ServiceCallInterface::IPCCenter::SCIID_Send,
		(u32)dst,
		(u32)vaddr
	);
}

int do_receive(pid_t src, addr_t vaddr)
{
	Resource * res = ResourceManager.RequestResource(
		ServiceCallInterface::IPCCenter::ResourceName
	);
	return res->CallSCI(
		ServiceCallInterface::IPCCenter::SCIID_Receive,
		(u32)src,
		(u32)vaddr
	);
}

void do_block(pid_t pid)
{
	Resource * res = ResourceManager.RequestResource(
		ServiceCallInterface::Scheduler::ResourceName
	);

	res->CallSCI(
		ServiceCallInterface::Scheduler::SCIID_Block,
		pid
	);
	return;
}

void do_wakeup(pid_t pid)
{
	//panic("Wake Up the %d process.",pid);
	Resource * res = ResourceManager.RequestResource(
		ServiceCallInterface::Scheduler::ResourceName
	);

	res->CallSCI(
		ServiceCallInterface::Scheduler::SCIID_WakeUp,
		pid
	);
	return;
}

int do_open(regs_t *regs)
{
	char * fname = (char *)GetCurrentProcess()->GetContext()->GetPhyAddress(regs->ebx);
	FileControlBlock * FCB = GetCurrentProcess()->GetFCB();

	return FCB->open(fname);
}

int do_close(regs_t *regs)
{
	return 0;
}

int do_read(regs_t *regs)
{
	int fd = regs->ebx;
	addr_t buf = regs->ecx;
	size_t len = regs->edx;
	Process * target = GetCurrentProcess();

	target->GetFCB()->read(fd, buf, len);

	/* Block current process until driver called FinsihRequest */
	Resource * res = ResourceManager.RequestResource(
		ServiceCallInterface::Scheduler::ResourceName
	);
	res->CallSCI(
		ServiceCallInterface::Scheduler::SCIID_Block,
		target->GetPID()
	);

	return EJUSTRETURN;
}

int do_write(regs_t *regs)
{
	int fd = regs->ebx;
	addr_t buf = regs->ecx;
	size_t len = regs->edx;
	Process * target = GetCurrentProcess();

	target->GetFCB()->write(fd, buf, len);

	/* Block current process until driver called FinsihRequest */
	Resource * res = ResourceManager.RequestResource(
		ServiceCallInterface::Scheduler::ResourceName
	);
	res->CallSCI(
		ServiceCallInterface::Scheduler::SCIID_Block,
		target->GetPID()
	);

	return EJUSTRETURN;
}

int do_lseek(regs_t *regs)
{
	return 0;
}

/*
	ebx	name
	ecx	major
	edx	cnt
*/
int do_mkdev(regs_t *regs)
{
	const char * name = (const char *)
		GetCurrentProcess()->GetContext()->GetPhyAddress(regs->ebx);
	pid_t pid = GetCurrentProcess()->GetPID();
	u16 major = (u16)(regs->ecx & 0xFFFF);
	u16 cnt = (u16)(regs->edx & 0xFFFF);

	// Get UDevFS
	Resource * res = ResourceManager.RequestResource(
		ServiceCallInterface::FileSystem::ResourceName
	);
	UDevFS * devfs = (UDevFS *)res->CallSCI(
		ServiceCallInterface::FileSystem::SCIID_GetFileSystem,
		FS_UDEV
	);

	// Create device
	File * file = devfs->CreateDevice(name, pid, major, cnt);
	
	if(file==NULL)
		kprintf("KNL: Process %d error create device file \"%s\".\n", GetCurrentProcess()->GetPID(), name);
	else
		kprintf("KNL: Process %d create device file \"%s\".\n", GetCurrentProcess()->GetPID(), name);
	
	return 0;
}

int do_gettimeofday(regs_t *regs)
{
	return ClockIRQAction::GetInstance()->GetCMOSTime();
}

int do_MapAddress(regs_t *regs)
{
	addr_t vaddr = regs->ebx;
	size_t size = regs->ecx;
	addr_t paddr = regs->edx;
	int order = (size+PAGE_SIZE-1)>>12;

	GetCurrentProcess()->GetContext()->MapPages(vaddr, paddr, order);

kprintf("KNL: Process %d map virtual address 0x%x to physical address 0x%x.\n", GetCurrentProcess()->GetPID(), vaddr, paddr);

	return 0;
}

int do_UnmapAddress(regs_t *regs)
{
	addr_t vaddr = regs->ebx;
	size_t size = regs->ecx;
	int order = (size+PAGE_SIZE-1)>>12;

	kprintf("KNL: Process %d unmap address 0x%x length 0x%x.\n", GetCurrentProcess()->GetPID(), vaddr, size);

	return 0;
}

int do_RegisterIRQ(regs_t *regs)
{
	u32 irq = regs->ebx;
	DefaultIRQAction * action = new DefaultIRQAction(
		GetCurrentProcess()->GetPID(),
		SA_REOPEN_IRQ
	);
	
	Resource * res = ResourceManager.RequestResource(
		ServiceCallInterface::ActionManager::ResourceName
	);
	bool bSuccess = (bool)res->CallSCI(
		ServiceCallInterface::ActionManager::SCIID_RegisterIRQAction,
		irq,
		(u32)action
	);

	kprintf("KNL: pid %d Register IRQ%d. [%s]\n", GetCurrentProcess()->GetPID(), regs->ebx, (bSuccess?"OK":"Failed"));
	
	if(bSuccess)
	{
		res = ResourceManager.RequestResource(
			ServiceCallInterface::IntrGateManager::ResourceName
		);
		res->CallSCI(
			ServiceCallInterface::IntrGateManager::SCIID_EnableIRQ,
			irq
		);

		return 0;
	}
	else 
		return EEXIST;
}

int do_UnRegisterIRQ(regs_t *regs)
{
	return 0;
}

int do_FinishRequest(regs_t *regs)
{
	Context *context = GetCurrentProcess()->GetContext();
	int ReturnValue = regs->ecx;
	RequestPackage *IRP = (RequestPackage *)context->GetPhyAddress(
		regs->ebx
	);	

	/* Reset memory map of driver */
	int order = ((addr_t)IRP->GetRequestSize())>>12;
	context->UnmapPages((addr_t)IRP->GetRequestBuffer(), order);
	
	/* Set request process's return value */
	Resource * res = ResourceManager.RequestResource(
		ServiceCallInterface::ProcessManager::ResourceName
	);
	Process * request_proc = (Process *)res->CallSCI(
		ServiceCallInterface::ProcessManager::SCIID_GetProcessByPid,
		IRP->GetRequestPID()
	);
	request_proc->GetThread()->SetRegisterValue(REGS_EAX, ReturnValue);

	/* Wake up target process, blocked by fs syscall */
	res = ResourceManager.RequestResource(
		ServiceCallInterface::Scheduler::ResourceName
	);
	res->CallSCI(
		ServiceCallInterface::Scheduler::SCIID_WakeUp,
		IRP->GetRequestPID()
	);

	return 0;
}

int do_SetPriority(regs_t *regs)
{
	int priority = regs->ebx;
	int cur_priority = GetCurrentProcess()->GetPriority();

	if(cur_priority<priority)
	{
		GetCurrentProcess()->SetPriority(priority);
	}
	
	return 0;
}

};

